﻿namespace NJQuery {
	using System;
	using System.Collections.Generic;
	using System.IO;
	using System.Web;
	using System.Web.UI;
	using System.Web.UI.WebControls;
	using System.Web.UI.HtmlControls;
	using System.ComponentModel;
	using System.Globalization;
	using System.Diagnostics.CodeAnalysis;

	/// <summary>
	/// Server control used to handle style and script referencing in addition to unified generation of load and ready scripts.
	/// The position of this control dictates where the initial page scripts are generated in the HTML body.
	/// </summary>
	[ParseChildren(true), PersistChildren(false)]
	public class Manager : Control {


		#region Constants: Property defaults

		private const MasterBoolean defaultUseCssStyling = MasterBoolean.ByControl;
		private const ScriptRendering defaultScriptRendering = ScriptRendering.DirectOutput;

		#endregion


		#region Constants: Special Style Names

		private const string uiUnifiedStyleName = "jquery.ui";
		private const string uiThemeStyleName = "jquery.ui.theme";
		private const string uiCustomThemeStyleName = "jquery.ui.customtheme";

		#endregion


		#region Static Fields: Configuration Shortcut

		private static readonly string defaultTheme = Settings.Current.ClientStyles.Themes.DefaultTheme;

		#endregion


		#region Static Methods: Protected helpers

		/// <summary>
		/// Gets the <see cref="Manager"/> from the page's items dictionary.
		/// </summary>
		/// <param name="page">The <see cref="Page"/> the rendering control resides within.</param>
		/// <returns>The jQuery <see cref="Manager"/> control contained in the items dictionary.</returns>
		/// <exception cref="ArgumentNullException">A page reference is required.</exception>
		protected static Manager Get(Page page) {
			if(page == null) throw new ArgumentNullException("page");

			return page.Items[typeof(Manager)] as Manager;
		}

		/// <summary>
		/// Sets the <see cref="Manager"/> into the page's items dictionary.
		/// </summary>
		/// <param name="page">The <see cref="Page"/> the rendering control resides within.</param>
		/// <param name="value">The jQuery <see cref="Manager"/> control to place into the items dictionary.</param>
		protected static void Set(Page page, Manager value) {
			if(page == null) throw new ArgumentNullException("page");
			if(value == null) throw new ArgumentNullException("value");

			page.Items[typeof(Manager)] = value;
		}

		#endregion


		#region Static Methods: Public

		/// <summary>
		/// Returns the required single instance of the <see cref="Manager"/> control.
		/// </summary>
		/// <param name="page">The <see cref="Page"/> the rendering control resides within.</param>
		/// <returns>The required single instance of the jQuery <see cref="Manager"/> control.</returns>
		/// <exception cref="ArgumentNullException">A page reference is required.</exception>
		/// <exception cref="InvalidOperationException">Page must contain an instance of <see cref="Manager"/>.</exception>
		public static Manager GetCurrent(Page page) {
			Manager manager = Get(page);
			if(manager == null) throw new InvalidOperationException("Page must contain an instance of " + typeof(Manager).FullName + ".");
			return manager;
		}

		#endregion


		#region Static Methods: Private routines

		private static void AddStyleRefToHeader(ControlCollection headerControls, string style, int index) {

			HtmlLink link = new HtmlLink();

			link.Attributes["rel"] = "stylesheet";
			link.Attributes["type"] = "text/css";
			link.Href = style;

			headerControls.AddAt(index, link);
		}

		private static void GetListDeltas(List<string> previousItems, List<string> currentItems, out List<string> removedItems, out List<string> addedItems) {
			addedItems = new List<string>();
			removedItems = new List<string>();

			foreach(string previousItem in previousItems)
				if(!currentItems.Contains(previousItem)) removedItems.Add(previousItem);

			foreach(string currentItem in currentItems)
				if(!previousItems.Contains(currentItem)) addedItems.Add(currentItem);

		}

		#endregion


		#region Fields

		private List<IManagedControl> readyScriptControls;
		private List<IManagedControl> startupScriptControls;
		private List<IManagedControl> submitScriptControls;
		private List<string> registeredScriptUrls;
		private bool themeRegistered;
		private List<string> registeredStyleUrls;

		#endregion


		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="Manager"/> class.
		/// </summary>
		public Manager() {
			readyScriptControls = new List<IManagedControl>();
			startupScriptControls = new List<IManagedControl>();
			submitScriptControls = new List<IManagedControl>();
			registeredScriptUrls = new List<string>();
			registeredStyleUrls = new List<string>();
		}

		#endregion


		#region Properties

		/// <summary>
		/// Gets or sets the theme in which to display all jQuery UI widgets.
		/// </summary>
		[DefaultValue(""), Category("Appearance")]
		public virtual string Theme {
			get { return ViewState["Theme"] as string ?? string.Empty; }
			set { ViewState["Theme"] = value; }
		}

		/// <summary>
		/// Gets or sets a value instructing all jQuery server controls on the page (or control by control by default) not to render style defined as HTML attributes on the element, rather requiring the site's stylesheet to handle functionality required to properly render the client UI.
		/// </summary>
		[DefaultValue(defaultUseCssStyling), Category("Behavior")]
		public virtual MasterBoolean UseCssStyling {
			get { return ViewState["UseCssStyling"] as MasterBoolean? ?? defaultUseCssStyling; }
			set { ViewState["UseCssStyling"] = value; }
		}

		[DefaultValue(defaultScriptRendering), Category("Behavior")]
		public virtual ScriptRendering ScriptRendering {
			get { return ViewState["ScriptRendering"] as ScriptRendering? ?? defaultScriptRendering; }
			set { ViewState["ScriptRendering"] = value; }
		}

		/// <summary>
		/// Gets a list of stylesheet URLs that have been registered by various server controls.
		/// </summary>
		public virtual IList<string> RegisteredStyleUrls {
			get { return registeredStyleUrls; }
		}

		/// <summary>
		/// Gets a list of script library URLs that have been registered by various server controls.
		/// </summary>
		public virtual IList<string> RegisteredScriptUrls {
			get { return registeredScriptUrls; }
		}

		/// <summary>
		/// Gets whether or not the page is involved in an Asynchornous postback that will result in a partial update of the client page.
		/// </summary>
		public virtual bool IsInAsyncPostBack {
			get {
				ScriptManager manager = Page.Items[typeof(ScriptManager)] as ScriptManager;
				return manager != null && manager.IsInAsyncPostBack;
			}
		}

		/// <summary>
		/// Gets the name of the HTML Meta tag placed in the header that contains the library's version number.
		/// This tag is used as client-side place holder in the HTML Head to ensure stylesheets are kept together on asynchronous postback.
		/// </summary>
		public virtual string VersionMetaName {
			get { return "njQuery.Version"; }
		}

		/// <summary>
		/// Gets the assembly version used in the HTML Meta tag with the name specified by <see cref="VersionMetaName" />.
		/// </summary>
		public virtual Version Version {
			get {
				return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
			}
		}

		#endregion


		#region Methods: Registration

		/// <summary>
		/// Registers a control to receive <see cref="IManagedControl.RenderReadyScript"/> calls at the appropriate time.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <typeparam name="T">The control type being registered. Must inherit <see cref="Control"/> and implement <see cref="IManagedControl"/>.</typeparam>
		/// <param name="control">The jQuery server control to receive the appropriate jQuery render calls.</param>
		public virtual void RegisterForReadyScript<T>(T control) where T : Control, IManagedControl {
			readyScriptControls.Add(control);
		}

		/// <summary>
		/// Registers a control to receive <see cref="IManagedControl.RenderStartupScript"/> calls at the appropriate time.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <typeparam name="T">The control type being registered. Must inherit <see cref="Control"/> and implement <see cref="IManagedControl"/>.</typeparam>
		/// <param name="control">The jQuery server control to receive the appropriate jQuery render calls.</param>
		public virtual void RegisterForStartupScript<T>(T control) where T : Control, IManagedControl {
			startupScriptControls.Add(control);
		}

		/// <summary>
		/// Registers a control to receive <see cref="IManagedControl.RenderSubmitScript"/> calls at the appropriate time.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <typeparam name="T">The control type being registered. Must inherit <see cref="Control"/> and implement <see cref="IManagedControl"/>.</typeparam>
		/// <param name="control">The jQuery server control to receive the appropriate jQuery render calls.</param>
		public virtual void RegisterForSubmitScript<T>(T control) where T : Control, IManagedControl {
			submitScriptControls.Add(control);
		}

		/// <summary>
		/// Registers a required script library to be referenced from the rendered page.
		/// </summary>
		/// <param name="script">The <see cref="ClientScript"/> with which to create or look up the URL based on configuration.</param>
		public void RegisterClientScript(ClientScript script) {
			if(script.IsEmpty) return;
			RegisterClientScript(script.RegisterName);
		}

		/// <summary>
		/// Registers a required script library to be referenced from the rendered page.
		/// </summary>
		/// <param name="name">The invariant name of the client script with wich to create or look up the URL based on configuration.</param>
		protected virtual void RegisterClientScript(string name) {

			Configuration.ClientScriptsSection scriptsSettings = Settings.Current.ClientScripts;
			Configuration.ClientScript script = scriptsSettings.References[name];

			string url;
			if(script != null && !string.IsNullOrEmpty(script.Url))
				url = ResolveUrl(scriptsSettings.BaseUrl, script.Url);
			else {
				url = string.Format(CultureInfo.InvariantCulture, "./{0}.js", name);
				url = ResolveUrl(scriptsSettings.BaseUrl, url);
			}

			if(!registeredScriptUrls.Contains(url)) registeredScriptUrls.Add(url);
		}

		/// <summary>
		/// Registers a required stylesheet to be linked from the rendered page's header.
		/// </summary>
		/// <param name="style">The <see cref="ClientStyle"/> with which to create or look up the URL based on configuration.</param>
		public void RegisterClientStyle(ClientStyle style) {
			if(style.IsEmpty) return;
			RegisterClientStyle(style.RegisterName);
		}

		/// <summary>
		/// Registers a required stylesheet to be linked from the rendered page's header.
		/// </summary>
		/// <param name="name">The invariant name of the stylesheet with which to create or look up the URL based on configuration.</param>
		protected virtual void RegisterClientStyle(string name) {

			Configuration.ClientStylesSection stylesSettings = Settings.Current.ClientStyles;
			Configuration.ClientStyle style = stylesSettings.References[name];

			string theme = Theme;
			if(string.IsNullOrEmpty(theme)) theme = defaultTheme;

			string url;
			if(style != null && !string.IsNullOrEmpty(style.Url))
				url = ResolveUrl(stylesSettings.BaseUrl, style.Url);
			else if(name == uiThemeStyleName || name == uiCustomThemeStyleName) {
				url = string.Format(CultureInfo.InvariantCulture, "./{0}.{1}.css", uiThemeStyleName, theme);
				url = ResolveUrl(stylesSettings.BaseUrl, url);
			} else {
				url = string.Format(CultureInfo.InvariantCulture, "./{0}.css", name);
				url = ResolveUrl(stylesSettings.BaseUrl, url);
			}

			// Special case when applying unified style: if theme is flagged "custom" and use base theme instead
			if(name == uiUnifiedStyleName) {
				if(stylesSettings.Themes.CustomThemes.Contains(theme))
					theme = "base";
			}

			//TODO: Get TheMuuj to make this more efficient
			if(url.Contains("*theme*")) url = url.Replace("*theme*", theme);

			if(!registeredStyleUrls.Contains(url)) registeredStyleUrls.Add(url);
		}

		/// <summary>
		/// Registers the currently active theme stylesheet file to be linked from the rendered page's header.
		/// </summary>
		public virtual void RegisterTheme() {
			themeRegistered = true;
		}

		/// <summary>
		/// Registers the current theme's stylesheet for inclusion in the rendered page's head section.
		/// This method is used primarily by control developers.
		/// </summary>
		protected virtual void RegisterThemeClientStyle() {
			Configuration.ClientStylesSection stylesSettings = Settings.Current.ClientStyles;

			string theme = Theme;
			if(string.IsNullOrEmpty(theme)) theme = defaultTheme;

			if(!string.IsNullOrEmpty(theme)) {
				string specificThemeStyleName = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", uiThemeStyleName, theme);

				if(stylesSettings.References.Contains(specificThemeStyleName))
					RegisterClientStyle(specificThemeStyleName); // a specific URL exists for the exact theme

				else if(stylesSettings.Themes.CustomThemes.Contains(theme))
					RegisterClientStyle(uiCustomThemeStyleName); // it is designated as custom, so don't even check if unified URL exists or is theme-specific

				else {

					// Check to see if unified URL exists and is theme-specific
					Configuration.ClientStyle unifiedUIStyle = stylesSettings.References[uiUnifiedStyleName];
					if(unifiedUIStyle != null && !string.IsNullOrEmpty(unifiedUIStyle.Url) && unifiedUIStyle.Url.Contains("*theme*"))
						RegisterClientStyle(uiUnifiedStyleName); //Probably already registered in this case, but just in case...

					// Simply register as general theme
					else
						RegisterClientStyle(uiThemeStyleName);

				}
			}
		}

		#endregion


		#region Methods: Rendering hooks

		/// <summary>
		/// Raises the <see cref="Control.Init"/> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs"/> object that contains the event data.</param>
		protected override void OnInit(EventArgs e) {
			base.OnInit(e);

			if(DesignMode) return;

			if(Get(Page) != null) throw new InvalidOperationException("Page can contain only one instance of " + typeof(Manager).FullName + ".");
			Set(Page, this);

			Page.PreRenderComplete += new EventHandler(Page_PreRenderComplete);
		}

		/// <summary>
		/// Sends server control content to a provided <see cref="HtmlTextWriter"/> object, which writes the content to be rendered to the client.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="writer">A <see cref="HtmlTextWriter"/> that represents the output stream to render HTML content on the client.</param>
		protected override void Render(HtmlTextWriter writer) {
			if(writer == null) throw new ArgumentNullException("writer");

			Dictionary<Control, string> readyScripts = GetReadyScripts();
			Dictionary<Control, string> startupScripts = GetStartupScripts();

			if(IsInAsyncPostBack) {

				foreach(KeyValuePair<Control, string> script in readyScripts)
					ScriptManager.RegisterStartupScript(script.Key, typeof(Manager), string.Format(CultureInfo.InvariantCulture, "Ready_{0}", script.Key.ClientID), script.Value, true);
				foreach(KeyValuePair<Control, string> script in startupScripts)
					ScriptManager.RegisterStartupScript(Page, typeof(Manager), string.Format(CultureInfo.InvariantCulture, "Startup_{0}", script.Key.ClientID), script.Value, true);

				Dictionary<Control, string> submitScripts = GetSubmitScripts();
				foreach(KeyValuePair<Control, string> script in submitScripts)
					ScriptManager.RegisterOnSubmitStatement(Page, typeof(Manager), string.Format(CultureInfo.InvariantCulture, "Submit_{0}", script.Key.ClientID), script.Value);

			} else {
				writer.Indent--;
				writer.WriteLine();

				if(ScriptRendering == ScriptRendering.DirectOutput) {
					foreach(string scriptUrl in registeredScriptUrls) {
						writer.Write("<script type=\"text/javascript\" language=\"javascript\" src=\"");
						writer.Write(HttpUtility.HtmlAttributeEncode(scriptUrl));
						writer.WriteLine("\"></script>");
					}
				}

				writer.WriteLine("<script type=\"text/javascript\" language=\"javascript\">");
				writer.WriteLine("//<![CDATA[");

				if(readyScripts.Count != 0 || startupScripts.Count != 0) {
					writer.WriteLine("$(document).ready(function() {");
					foreach(string script in readyScripts.Values)
						writer.WriteLine(script);
					foreach(string script in startupScripts.Values)
						writer.WriteLine(script);
					writer.WriteLine("});");
				}

				//if(submitScripts.Count != 0) {
				//	writer.Write("$('#");
				//	writer.Write(Page.Form.ClientID);
				//	writer.WriteLine("').submit(function() {");
				//	foreach(string script in submitScripts.Values)
				//		writer.WriteLine(script);
				//	writer.WriteLine("});");
				//}

				writer.WriteLine("//]]>");
				writer.WriteLine("</script>");

			}


		}

		/// <summary>
		/// Adds all referenced script files into the page body on initial render or full page postback.
		/// This method is used primarily by control developers.
		/// </summary>
		protected virtual void ReferenceRegisteredScripts() {
			if(ScriptRendering != ScriptRendering.SystemRegistration) return;

			foreach(string scriptUrl in registeredScriptUrls)
				Page.ClientScript.RegisterClientScriptInclude(scriptUrl, scriptUrl);

		}

		/// <summary>
		/// Adds all referenced stylesheet into the page's header on intial render or full page postback.
		/// This method is used primarily by control developers.
		/// </summary>
		protected virtual void ReferenceRegisteredStyles() {

			StyleLinks styleLinks = StyleLinks.GetCurrent(Page);
			if(styleLinks == null) {

				ControlCollection headerControls = Page.Header.Controls;
				int index = 0;

				foreach(string styleUrl in registeredStyleUrls)
					AddStyleRefToHeader(headerControls, styleUrl, index++);

				HtmlMeta versionMeta = new HtmlMeta();
				versionMeta.Name = VersionMetaName;
				versionMeta.Content = this.Version.ToString();
				headerControls.AddAt(index, versionMeta);

			}

		}

		/// <summary>
		/// Renders client script that changes the list of registered stylesheet files on asynchronous postback.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		/// <param name="removedUrls">A list of stylesheet URLs to be removed from the client page.</param>
		/// <param name="addedUrls">A list of stylesheet URLs to be added to the client page.</param>
		protected virtual void RenderStyleModificationScript(ScriptBuilder script, IList<string> removedUrls, IList<string> addedUrls) {

			string encodedVersionMetaName = ScriptBuilder.JavaScriptStringEncode(VersionMetaName);

			// Remove files no longer referenced
			foreach(string removedUrl in removedUrls) {

				script.NewLine();

				script.Append("$('link[href=");
				script.Append(ScriptBuilder.JavaScriptStringEncode(removedUrl));
				script.Append("]').replaceWith('');");

			}

			// Add files not previously referenced
			if(addedUrls.Count != 0) {

				script.NewLine();

				script.Append("$('meta[name=");
				script.Append(encodedVersionMetaName);
				script.Append("]').before('");

				foreach(string addedUrl in addedUrls) {
					script.Append("<link type=\"text/css\" rel=\"stylesheet\" href=\"");
					script.Append(ScriptBuilder.JavaScriptStringEncode(HttpUtility.HtmlAttributeEncode(addedUrl)));
					script.Append("\" />");
				}

				script.Append("');");
			}

			// Hack for IE due to it applying stylesheets in creation order rather than DOM order and
			// that it won't update unless a link tag with rel and href is added to the end of the Head
			if(Page.Request.Browser.Browser == "IE") {

				script.NewLine();

				// Make a temporary head to hold links
				script.Append("var head = document.createElement('head');");

				// Add stylesheets and version meta to temporary head
				script.Append("$('link[rel=stylesheet],meta[name=");
				script.Append(encodedVersionMetaName);
				script.Append("]').each(function(){$(head).append(this);});");

				// Clear stylesheets and version meta from real head
				script.Append("$('link[rel=stylesheet],meta[name=");
				script.Append(encodedVersionMetaName);
				script.Append("]').replaceWith('');");

				// Add contents of temporary head to real head to make DOM order = Create order
				script.Append("$('head').append($(head).html());");

				// Create a dummy link in a way that triggers reprocessing of styles
				script.Append("var link = document.createElement('link');link.rel = 'stylesheet';link.href = 'about:blank';");
				script.Append("document.getElementsByTagName('head')[0].appendChild(link);");

				// Remove dummy link
				script.Append("$(link).replaceWith('');");

			}

		}

		/// <summary>
		/// Renders client script that adds to the list of registered script file references on asynchronous postback.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		/// <param name="addedUrls">A list of script URLs to be added to the client page.</param>
		protected virtual void RenderScriptRefAdditionScript(ScriptBuilder script, IList<string> addedUrls) {
			if(addedUrls.Count == 0) return;

			script.Append("var scriptRef;");

			foreach(string addedUrl in addedUrls) {

				script.NewLine();

				script.Append("scriptRef=document.createElement('script');");
				script.Append("scriptRef.type='text/javascript';");
				script.Append("scriptRef.language='javascript';");

				script.Append("scriptRef.src=");
				script.AppendValue(addedUrl);
				script.Append(";");

				script.Append("document.getElementsByTagName('head')[0].appendChild(scriptRef);");

			}

		}

		#endregion


		#region Methods: Private routines

		private Dictionary<Control, string> GetReadyScripts() {
			Dictionary<Control, string> scripts = new Dictionary<Control, string>();

			foreach(IManagedControl control in readyScriptControls) {
				Control systemControl = control as Control;
				if(systemControl == null || !systemControl.Visible) continue;

				ScriptBuilder script = new ScriptBuilder();
				script.Indent++;

				control.RenderReadyScript(script);
				if(script.Length != 0) scripts.Add(systemControl, script.ToString());
			}

			return scripts;
		}

		private Dictionary<Control, string> GetStartupScripts() {
			Dictionary<Control, string> scripts = new Dictionary<Control, string>();

			foreach(IManagedControl control in startupScriptControls) {
				Control systemControl = control as Control;
				if(systemControl == null || !systemControl.Visible) continue;

				ScriptBuilder script = new ScriptBuilder();
				script.Indent++;

				control.RenderStartupScript(script);
				if(script.Length != 0) scripts.Add(systemControl, script.ToString());

			}

			return scripts;
		}

		private Dictionary<Control, string> GetSubmitScripts() {
			Dictionary<Control, string> scripts = new Dictionary<Control, string>();

			foreach(IManagedControl control in submitScriptControls) {
				Control systemControl = control as Control;
				if(systemControl == null || !systemControl.Visible) continue;

				ScriptBuilder script = new ScriptBuilder();
				script.Indent++;

				control.RenderSubmitScript(script);
				if(script.Length != 0) scripts.Add(systemControl, script.ToString());

			}

			return scripts;
		}

		private string ResolveUrl(string baseUrl, string url) {
			if(string.IsNullOrEmpty(url)) return baseUrl;

			if(!string.IsNullOrEmpty(baseUrl) && (url.StartsWith("./", StringComparison.Ordinal) || url.StartsWith("../", StringComparison.Ordinal)))
				url = Path.Combine(baseUrl, url);

			return ResolveUrl(url);
		}

		#endregion


		#region Event Handlers

		private void Page_PreRenderComplete(object sender, EventArgs e) {
			const string styleFilesViewStateKey = "StyleFiles";
			const string scriptFilesViewStateKey = "ScriptFiles";

			// Register Theme
			if(themeRegistered) RegisterThemeClientStyle();

			if(IsInAsyncPostBack) {

				// Get changes in styles (must be before scripts)
				List<string> removedStyles; List<string> addedStyles;
				GetListDeltas((List<string>)ViewState[styleFilesViewStateKey], registeredStyleUrls, out removedStyles, out addedStyles);

				// If per chance core is not registered, it will be needed to change the scripts
				if(removedStyles.Count != 0 || addedStyles.Count != 0)
					RegisterClientScript(ClientScript.Core);

				// Get changes in scripts
				List<string> removedScripts; List<string> addedScripts;
				GetListDeltas((List<string>)ViewState[scriptFilesViewStateKey], registeredScriptUrls, out removedScripts, out addedScripts);
				registeredScriptUrls.AddRange(removedScripts); // Not removing script files so track them for next time

				// Register script modification script (must be before styles)
				if(addedScripts.Count != 0) {
					ScriptBuilder script = new ScriptBuilder();
					RenderScriptRefAdditionScript(script, addedScripts);
					if(script.Length != 0) ScriptManager.RegisterStartupScript(Page, typeof(Manager), scriptFilesViewStateKey, script.ToString(), true);
				}

				// Register style modification script
				if(removedStyles.Count != 0 || addedStyles.Count != 0) {
					ScriptBuilder script = new ScriptBuilder();
					RenderStyleModificationScript(script, removedStyles, addedStyles);
					if(script.Length != 0) ScriptManager.RegisterStartupScript(Page, typeof(Manager), styleFilesViewStateKey, script.ToString(), true);
				}

			} else {

				ReferenceRegisteredStyles();
				ReferenceRegisteredScripts();

				Dictionary<Control, string> submitScripts = GetSubmitScripts();
				ClientScriptManager clientScript = Page.ClientScript;
				foreach(KeyValuePair<Control, string> script in submitScripts)
					Page.ClientScript.RegisterOnSubmitStatement(typeof(Manager), string.Format(CultureInfo.InvariantCulture, "Submit_{0}", script.Key.ClientID), script.Value);

			}

			ViewState[styleFilesViewStateKey] = registeredStyleUrls;
			ViewState[scriptFilesViewStateKey] = registeredScriptUrls;

		}

		#endregion


	}

}